//
// Created by twetec on 25-8-17.
//

#include "handle_redis_probe_task.h"

#include <hiredis/hiredis.h>
#include <boost/json.hpp>

#include <bs_thread_pool/BS_thread_pool.hpp>

using namespace std::chrono_literals;

namespace network_prober::probe
{
// 线程池
static BS::thread_pool s_iopool{1000};

// RedisProbeIPv4::RedisProbeIPv4(
//     std::string_view interface,
//     std::string_view target,
//     std::uint32_t port,
//     std::string_view userName,
//     std::string_view password,
//     std::string_view databaseName,
//     std::string_view queryStatement
//     )
//     : m_interface(interface), m_target(target), m_port(port), m_userName(userName),
//       m_password(password), m_databaseName(databaseName), m_queryStatement(queryStatement)
// {
// }
//
// void RedisProbeIPv4::setOnProbedCallback(std::function<void(std::int64_t)> callback)
// {
//     m_onProbedCallback = std::move(callback);
// }
//
// void RedisProbeIPv4::probe()
// {
//     // 建立连接时，开始计时
//     m_probeStartTime = std::chrono::steady_clock::now();
//
//     // 指向当前对象的shared_ptr
//     auto self{shared_from_this()};
//
//     // 发起探测
//     m_onProbedCallbackFuture = std::async(std::launch::async, [this, self] {
//         // 连接配置
//         redisOptions options{0};
//         options.type = REDIS_CONN_TCP;  // 要求TCP方式连接
//         options.endpoint.tcp.ip = m_target.c_str(); // 目标redis服务地址
//         options.endpoint.tcp.port = m_port;   // 目标redis服务端口
//         options.endpoint.tcp.source_addr = m_interface.c_str(); // 客户端地址
//         options.options |= REDIS_OPT_PREFER_IPV4;   // 在dns时，推荐查找ipv6
//         options.options |= REDIS_OPT_REUSEADDR; // 重用本地地址
//
//         // 设置连接超时时间
//         timeval connectTimeout{.tv_sec = 13, .tv_usec = 0};
//         options.connect_timeout = &connectTimeout;
//
//         // 设置命令执行的超时时间
//         timeval commandTimeout{.tv_sec = 13, .tv_usec = 0};
//         options.command_timeout = &commandTimeout;
//
//         // 基础TCP连接
//         redisContext* context = redisConnectWithOptions(&options);
//         if (context == nullptr || context->err) {
//             // 报错信息
//             SPDLOG_INFO("Redis连接失败：{}", context->errstr);
//             // 释放资源
//             if (context != nullptr) redisFree(context);
//             // 回调结果
//             m_onProbedCallback(-1);
//             // 结束执行
//             return;
//         }
//
//         // 发送指令
//         redisReply* reply = static_cast<redisReply*>(redisCommand(context, "PING"));
//         // 如果错误发生，reply会为null,并且context->err会被设置
//         if (reply == nullptr || context->err) {
//             // 报错信息
//             SPDLOG_INFO("Redis指令执行失败：{}", context->errstr);
//             // 释放资源
//             if (context != nullptr) redisFree(context);
//             // 回调结果
//             m_onProbedCallback(-1);
//             // 结束执行
//             return;
//         }
//
//         // 释放响应
//         freeReplyObject(reply);
//         // 释放资源
//         redisFree(context);
//
//         // 回调结果
//         m_onProbedCallback(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_probeStartTime).count());
//     });
// }
//
// RedisProbeIPv4::~RedisProbeIPv4()
// {
// }

HandleRedisProbeIPv4Awaiter::HandleRedisProbeIPv4Awaiter(const database::ProbeTaskModel &probeTask)
    : m_interface(probeTask.interface), m_target(probeTask.address), m_port(probeTask.port),
      m_userName(probeTask.userName),
      m_password(probeTask.password), m_databaseName(probeTask.databaseName), m_queryStatement(probeTask.queryStatement)
{
}

bool HandleRedisProbeIPv4Awaiter::await_ready()
{
    return false;
}

void HandleRedisProbeIPv4Awaiter::await_suspend(std::coroutine_handle<> handle)
{
    s_iopool.submit_task([this, handle] {
        // 线程锁
        std::mutex mtx;
        std::condition_variable cv;

        // 请求计时 - 开始时间
        std::chrono::time_point<std::chrono::steady_clock> probeStartTime = std::chrono::steady_clock::now();

        // 发起探测
        std::future future = std::async(std::launch::async, [this, &probeStartTime, &mtx, &cv] {
            // 连接配置
            redisOptions options{0};
            options.type = REDIS_CONN_TCP; // 要求TCP方式连接
            options.endpoint.tcp.ip = m_target.c_str(); // 目标redis服务地址
            options.endpoint.tcp.port = m_port; // 目标redis服务端口
            options.endpoint.tcp.source_addr = m_interface.c_str(); // 客户端地址
            options.options |= REDIS_OPT_PREFER_IPV4; // 在dns时，推荐查找ipv6
            options.options |= REDIS_OPT_REUSEADDR; // 重用本地地址

            // 设置连接超时时间
            timeval connectTimeout{.tv_sec = 6, .tv_usec = 0};
            options.connect_timeout = &connectTimeout;

            // 设置命令执行的超时时间
            timeval commandTimeout{.tv_sec = 6, .tv_usec = 0};
            options.command_timeout = &commandTimeout;

            // 基础TCP连接
            redisContext *context = redisConnectWithOptions(&options);
            if (context == nullptr || context->err) {
                // 报错信息
                SPDLOG_INFO("Redis连接失败：{}", context->errstr);
                // 释放资源
                if (context != nullptr) redisFree(context);
                // 加锁
                std::unique_lock<std::mutex> lock(mtx);
                // 保存延迟
                m_delay = -1;
                // 通知延迟保存成功
                cv.notify_one();
                // 结束执行
                return;
            }

            // 发送指令
            redisReply *reply = static_cast<redisReply *>(redisCommand(context, "PING"));

            // 如果错误发生，reply会为null,并且context->err会被设置
            if (reply == nullptr || context->err) {
                // 报错信息
                SPDLOG_INFO("Redis指令执行失败：{}", context->errstr);
                // 释放资源
                if (context != nullptr)
                redisFree(context);
                // 加锁
                std::unique_lock<std::mutex> lock(mtx);
                // 保存延迟
                m_delay = -1;
                // 通知延迟保存成功
                cv.notify_one();
                // 结束执行
                return;
            }

            // 释放响应
            freeReplyObject(reply);
            // 释放资源
            redisFree(context);

            // 加锁
            std::unique_lock<std::mutex> lock(mtx);
            // 保存延迟
            m_delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - probeStartTime).count();
            // 通知延迟保存成功
            cv.notify_one();
        });

        // 等待响应（12s）
        std::unique_lock<std::mutex> lock(mtx);
        std::cv_status cvStatus = cv.wait_for(lock, 8s);

        // 如果等待超时,则将延迟置为-1
        if (cvStatus == std::cv_status::timeout) m_delay = -1;

        // 恢复暂停的协程
        handle.resume();
    });
}

std::int64_t HandleRedisProbeIPv4Awaiter::await_resume()
{
    return m_delay;
}


// RedisProbeIPv6::RedisProbeIPv6(
//     std::string_view interface,
//     std::string_view target,
//     std::uint32_t port,
//     std::string_view userName,
//     std::string_view password,
//     std::string_view databaseName,
//     std::string_view queryStatement
//     )
//     : m_interface(interface), m_target(target), m_port(port), m_userName(userName),
//       m_password(password), m_databaseName(databaseName), m_queryStatement(queryStatement)
// {
// }
//
// void RedisProbeIPv6::setOnProbedCallback(std::function<void(std::int64_t)> callback)
// {
//     m_onProbedCallback = std::move(callback);
// }
//
// void RedisProbeIPv6::probe()
// {
//     // 建立连接时，开始计时
//     m_probeStartTime = std::chrono::steady_clock::now();
//
//     // 指向当前对象的shared_ptr
//     auto self{shared_from_this()};
//
//     // 发起探测
//     m_onProbedCallbackFuture = std::async(std::launch::async, [this, self] {
//         // 连接配置
//         redisOptions options{0};
//         options.type = REDIS_CONN_TCP;  // 要求TCP方式连接
//         options.endpoint.tcp.ip = m_target.c_str(); // 目标redis服务地址
//         options.endpoint.tcp.port = m_port;   // 目标redis服务端口
//         options.endpoint.tcp.source_addr = m_interface.c_str(); // 客户端地址
//         options.options |= REDIS_OPT_PREFER_IPV6;   // 在dns时，推荐查找ipv6
//         options.options |= REDIS_OPT_REUSEADDR; // 重用本地地址
//
//         // 设置连接超时时间
//         timeval connectTimeout{.tv_sec = 13, .tv_usec = 0};
//         options.connect_timeout = &connectTimeout;
//
//         // 设置命令执行的超时时间
//         timeval commandTimeout{.tv_sec = 13, .tv_usec = 0};
//         options.command_timeout = &commandTimeout;
//
//         // 基础TCP连接
//         redisContext* context = redisConnectWithOptions(&options);
//         if (context == nullptr || context->err) {
//             // 报错信息
//             SPDLOG_INFO("Redis连接失败：{}", context->errstr);
//             // 释放资源
//             if (context != nullptr) redisFree(context);
//             // 回调结果
//             m_onProbedCallback(-1);
//             // 结束执行
//             return;
//         }
//
//         // 发送指令
//         redisReply* reply = static_cast<redisReply*>(redisCommand(context, "PING"));
//         // 如果错误发生，reply会为null,并且context->err会被设置
//         if (reply == nullptr || context->err) {
//             // 报错信息
//             SPDLOG_INFO("Redis指令执行失败：{}", context->errstr);
//             // 释放资源
//             if (context != nullptr) redisFree(context);
//             // 回调结果
//             m_onProbedCallback(-1);
//             // 结束执行
//             return;
//         }
//
//         // 释放响应
//         freeReplyObject(reply);
//         // 释放资源
//         redisFree(context);
//
//         // 回调结果
//         m_onProbedCallback(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_probeStartTime).count());
//     });
// }
//
// RedisProbeIPv6::~RedisProbeIPv6()
// {
// }

HandleRedisProbeIPv6Awaiter::HandleRedisProbeIPv6Awaiter(const database::ProbeTaskModel &probeTask)
    : m_interface(probeTask.interface), m_target(probeTask.address), m_port(probeTask.port),
      m_userName(probeTask.userName),
      m_password(probeTask.password), m_databaseName(probeTask.databaseName), m_queryStatement(probeTask.queryStatement)
{
}

bool HandleRedisProbeIPv6Awaiter::await_ready()
{
    return false;
}

void HandleRedisProbeIPv6Awaiter::await_suspend(std::coroutine_handle<> handle)
{
    s_iopool.submit_task([this, handle] {
        // 线程锁
        std::mutex mtx;
        std::condition_variable cv;

        // 请求计时 - 开始时间
        std::chrono::time_point<std::chrono::steady_clock> probeStartTime = std::chrono::steady_clock::now();

        std::future future = std::async(std::launch::async, [this, &probeStartTime, &mtx, &cv] {
            // 连接配置
            redisOptions options{0};
            options.type = REDIS_CONN_TCP;  // 要求TCP方式连接
            options.endpoint.tcp.ip = m_target.c_str(); // 目标redis服务地址
            options.endpoint.tcp.port = m_port;   // 目标redis服务端口
            // options.endpoint.tcp.source_addr = m_interface.c_str(); // 客户端地址
            options.options |= REDIS_OPT_PREFER_IPV6;   // 在dns时，推荐查找ipv6
            options.options |= REDIS_OPT_REUSEADDR; // 重用本地地址

            // 设置连接超时时间
            timeval connectTimeout{.tv_sec = 6, .tv_usec = 0};
            options.connect_timeout = &connectTimeout;

            // 设置命令执行的超时时间
            timeval commandTimeout{.tv_sec = 6, .tv_usec = 0};
            options.command_timeout = &commandTimeout;

            // 基础TCP连接
            redisContext* context = redisConnectWithOptions(&options);
            if (context == nullptr || context->err) {
                // 报错信息
                SPDLOG_INFO("Redis连接失败：{}", context->errstr);
                // 释放资源
                if (context != nullptr) redisFree(context);
                // 加锁
                std::unique_lock<std::mutex> lock(mtx);
                // 保存延迟
                m_delay = -1;
                // 通知延迟保存成功
                cv.notify_one();
                // 结束执行
                return;
            }

            // 发送指令
            redisReply* reply = static_cast<redisReply*>(redisCommand(context, "PING"));
            // 如果错误发生，reply会为null,并且context->err会被设置
            if (reply == nullptr || context->err) {
                // 报错信息
                SPDLOG_INFO("Redis指令执行失败：{}", context->errstr);
                // 释放资源
                if (context != nullptr) redisFree(context);
                // 加锁
                std::unique_lock<std::mutex> lock(mtx);
                // 保存延迟
                m_delay = -1;
                // 通知延迟保存成功
                cv.notify_one();
                // 结束执行
                return;
            }

            // 释放响应
            freeReplyObject(reply);
            // 释放资源
            redisFree(context);

            // 回调结果
            // 加锁
            std::unique_lock<std::mutex> lock(mtx);
            // 保存延迟
            m_delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - probeStartTime).count();
            // 通知延迟保存成功
            cv.notify_one();
        });

        // 等待响应（12s）
        std::unique_lock<std::mutex> lock(mtx);
        std::cv_status cvStatus = cv.wait_for(lock, 8s);

        // 如果等待超时,则将延迟置为-1
        if (cvStatus == std::cv_status::timeout) m_delay = -1;

        // 恢复暂停的协程
        handle.resume();
    });
}

std::int64_t HandleRedisProbeIPv6Awaiter::await_resume()
{
    return m_delay;
}
}
